package com.wang.springbootinit.service.impl.GenChart;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wang.springbootinit.Listener.ExcelNoModelDataListener;
import com.wang.springbootinit.common.CommonUtils;
import com.wang.springbootinit.common.ErrorCode;
import com.wang.springbootinit.constant.BISystemEnum;
import com.wang.springbootinit.constant.FileEnum;
import com.wang.springbootinit.constant.UserConstant;
import com.wang.springbootinit.exception.BusinessException;
import com.wang.springbootinit.exception.ThrowUtils;
import com.wang.springbootinit.manager.RedisLimiterManager;
import com.wang.springbootinit.mapper.SourceFileMapper;
import com.wang.springbootinit.minio.MinioUtil;
import com.wang.springbootinit.model.dto.chart.GenChartByAiRequest;
import com.wang.springbootinit.model.entity.Chart;
import com.wang.springbootinit.model.entity.Integral;
import com.wang.springbootinit.model.entity.SourceFile;
import com.wang.springbootinit.model.entity.User;
import com.wang.springbootinit.model.enums.ChartStatusEnum;
import com.wang.springbootinit.model.result.BIResponseResult;
import com.wang.springbootinit.model.vo.BIResponse;
import com.wang.springbootinit.service.ChartService;
import com.wang.springbootinit.service.GenChartService;
import com.wang.springbootinit.service.IntegralService;
import com.wang.springbootinit.service.UserService;
import com.wang.springbootinit.service.impl.UserServiceImpl;
import com.wang.springbootinit.sparkAi.AiAnalyseModel;
import com.wang.springbootinit.sparkAi.AiAnalyseQueryParam;
import com.wang.springbootinit.sparkAi.AnalyseServiceImpl;
import com.wang.springbootinit.utils.ExcelUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.framework.qual.RequiresQualifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;


/**
 * 图表分析
 */
@Slf4j
@Service
public class GenChartServiceImpl implements GenChartService {
    private static final Logger LOGGER = LoggerFactory.getLogger(GenChartServiceImpl.class);
    @Resource
    private ChartService chartService;
    @Resource
    private UserService userService;
    @Resource
    private RedisLimiterManager redisLimiterManager;
    @Resource
    private ThreadPoolExecutor threadPoolExecutor;
    @Resource
    private GenChartAfterHandler genChartAfterHandler;
    @Resource
    private AnalyseServiceImpl analyseService;
    @Resource
    private MinioUtil minioUtil;
    @Resource
    private SourceFileMapper sourceFileMapper;
    @Resource
    private IntegralService integralService;

    /**
     * 同步分析
     * @param multipartFile
     * @param param
     * @param request
     * @return
     */
    @Override
    public BIResponseResult genChartByAi(MultipartFile multipartFile, GenChartByAiRequest param, HttpServletRequest request) {
        try {
            //校验文件是否合法
            CommonUtils.verifyFile(multipartFile, param.getGoal(), param.getName());

            User loginUser = userService.getLoginUser(request);
            if (loginUser == null) {
                throw new BusinessException(BISystemEnum.NOLOGIN.getCode(), BISystemEnum.NOLOGIN.getMsg());
            }
            if(loginUser.getInvokeCount() <= 0){
                throw new BusinessException(ErrorCode.NO_INVOKE_COUNT);
            }
            //对接口进行限流
            redisLimiterManager.doRateLimit(UserConstant.RateLimit + loginUser.getId());

            String excelInfo = ExcelUtils.excelToCsv(multipartFile);
            //会话消息体
            String aiHandleMessage = GenChartCommonUtil.getAiHandleMessage(param.getGoal(), param.getChartType(), excelInfo);
            //获取ai返回的消息
            AiAnalyseModel aiAnalyseModel = analyseService.syncChat(new AiAnalyseQueryParam(aiHandleMessage));
            //后置处理
            BIResponseResult result = genChartAfterHandler.syncGenChartHandler(loginUser, param, aiAnalyseModel,excelInfo);
            if(aiAnalyseModel.getIsSuccess()){
                //上传并保存文件
                List<String> fileNameList = minioUtil.upload(multipartFile, FileEnum.SOURCE_FILE.getBucketName());
                SourceFile sourceFile = new SourceFile();
                sourceFile.setUserId(loginUser.getId());
                sourceFile.setChartId(result.getData().getChartId());
                if(CollectionUtil.isNotEmpty(fileNameList) && fileNameList.size()==2){
                    sourceFile.setOriginalFilename(fileNameList.get(0));
                    sourceFile.setObjectFileName(fileNameList.get(1));
                }else{
                    sourceFile.setOriginalFilename("error: minio保存失败");
                }
                sourceFileMapper.insert(sourceFile);
            }
            return result;
        } catch (Exception e) {
            LOGGER.error("GenChartServiceImpl，genChartByAi exception ",e);
            throw new BusinessException(BISystemEnum.FAIL.getCode(), e.getMessage());
        }
    }

    /**
     * 异步分析
     * @param multipartFile
     * @param genChartByAiRequest
     * @param request
     * @return
     */
    @Override
    public BIResponseResult genChartByAiAsync(MultipartFile multipartFile, GenChartByAiRequest genChartByAiRequest, HttpServletRequest request) {
        BIResponseResult biResponseResult = new BIResponseResult();

        //校验文件是否合法
        CommonUtils.verifyFile(multipartFile, genChartByAiRequest.getGoal(), genChartByAiRequest.getName());

        User loginUser = userService.getLoginUser(request);
        if (loginUser == null) {
            throw new BusinessException(BISystemEnum.NOLOGIN.getCode(), BISystemEnum.NOLOGIN.getMsg());
        }
        if(loginUser.getInvokeCount() <= 0){
            throw new BusinessException(ErrorCode.NO_INVOKE_COUNT);
        }
        //对接口进行限流
        redisLimiterManager.doRateLimit(UserConstant.RateLimit + loginUser.getId());

        String excelInfo = ExcelUtils.excelToCsv(multipartFile);
        //会话消息体
        String aiHandleMessage = GenChartCommonUtil.getAiHandleMessage(genChartByAiRequest.getGoal(), genChartByAiRequest.getChartType(), excelInfo);
        /**
         * 先将数据保存到数据库中，状态设置为 wait
         * 线程池执行智能分析 图表状态设置为running
         * 分析完成之后， 图表状态设置为succeed或failed 并将信息返回
         */

        //将生成的数据插入到数据库中
        Chart chart = new Chart();
        chart.setName(genChartByAiRequest.getName());
        chart.setGoal(genChartByAiRequest.getGoal());
        chart.setChartData(null);
        chart.setChartType(genChartByAiRequest.getChartType());
        chart.setUserId(loginUser.getId());
        chart.setStatus(ChartStatusEnum.WAIT.getValue()); //设置为等待状态
        chart.setExecMessage("图表还未生成");
        boolean saveResult = chartService.save(chart);
        ThrowUtils.throwIf(!saveResult, ErrorCode.SYSTEM_ERROR, "图表保存失败");

        CompletableFuture.runAsync(() -> {
            //执行状态  等待-》 执行中 -》 成功
            Chart chartRunning = new Chart();
            chartRunning.setId(chart.getId());
            chartRunning.setStatus(ChartStatusEnum.RUNNING.getValue()); //设置为执行中
            boolean updateById = chartService.updateById(chartRunning);
            if (!updateById) {
                handleErrorChart(chart.getId(), "更新图表状态 wait->running 失败");
                return;
            }

            //获取ai返回的消息
            AiAnalyseModel aiAnalyseModel = analyseService.syncChat(new AiAnalyseQueryParam(aiHandleMessage));
            //后置处理
            BIResponseResult result = genChartAfterHandler.syncGenChartHandler(loginUser, genChartByAiRequest, aiAnalyseModel,excelInfo);
            if(aiAnalyseModel.getIsSuccess()){
                //上传并保存文件
                List<String> fileNameList = minioUtil.upload(multipartFile, FileEnum.SOURCE_FILE.getBucketName());
                SourceFile sourceFile = new SourceFile();
                sourceFile.setUserId(loginUser.getId());
                sourceFile.setChartId(result.getData().getChartId());
                if(CollectionUtil.isNotEmpty(fileNameList) && fileNameList.size()==2){
                    sourceFile.setOriginalFilename(fileNameList.get(0));
                    sourceFile.setObjectFileName(fileNameList.get(1));
                }else{
                    sourceFile.setOriginalFilename("error: minio保存失败");
                }
                sourceFileMapper.insert(sourceFile);
            }
        }, threadPoolExecutor);

        //返回结果
        BIResponse biResponse = new BIResponse();
        biResponse.setChartId(chart.getId());

        biResponseResult.setData(biResponse);
        return biResponseResult;
    }
    /**
     * 处理图表状态保存失败
     *
     * @param chartId
     * @param errMessage
     */
    private void handleErrorChart(Long chartId, String errMessage) {
        //保存失败
        Chart tempChart = new Chart();
        tempChart.setId(chartId);
        tempChart.setStatus(ChartStatusEnum.FAILED.getValue()); //设置为失败
        tempChart.setExecMessage(errMessage);
        boolean tempRes = chartService.updateById(tempChart);
        if (!tempRes) {
            //一般不会失败
            LOGGER.error("更新图表状态失败");
        }
    }
}
